﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.IO;
using System.IO.Pipes;
using System.Xml;
using System.Data.SqlClient;
using System.Data.SqlTypes;

namespace OsmEncryptionService
{
    public partial class OsmEncryptionService : ServiceBase
    {
        private Thread _pipeThread;
        private string _password = null;

        public OsmEncryptionService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
#if DEBUG
            Debugger.Launch();
#endif
            _pipeThread = new Thread(new ThreadStart(MonitorPipe));
            _pipeThread.Start();
        }

        protected override void OnStop()
        {
            _pipeThread.Abort();
        }

        private void MonitorPipe()
        {
            try
            {
                SqlConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["SalesManager"].ConnectionString);
                conn.Open();

                PipeSecurity ps = new PipeSecurity();
                System.Security.Principal.SecurityIdentifier sid = new System.Security.Principal.SecurityIdentifier(System.Security.Principal.WellKnownSidType.WorldSid, null);
                PipeAccessRule psRule = new PipeAccessRule(sid, PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow);
                ps.AddAccessRule(psRule);

                using (NamedPipeServerStream pipeServer = new NamedPipeServerStream("OsmEncryptionPipe", PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.None, 1, 1, ps))
                {
                    while (true)
                    {
                        pipeServer.WaitForConnection();

                        using (XmlReader rdr = XmlReader.Create(pipeServer))
                        {
                            rdr.Read();

                            if (rdr.IsStartElement())
                            {
                                switch (rdr.Name)
                                {
                                    case "Encrypt":
                                        {
                                            string s = rdr.ReadString();

                                            try
                                            {
                                                if (conn.State != ConnectionState.Open)
                                                    conn.Open();
                                                SqlCommand cmm = conn.CreateCommand();
                                                cmm.CommandText = "select EncryptByCert(Cert_ID('OSMCertificate'), @cleartext)";
                                                cmm.Parameters.Add(new SqlParameter("@cleartext", s));
                                                SqlDataReader sqlRdr = cmm.ExecuteReader();
                                                sqlRdr.Read();

                                                byte[] buffer = sqlRdr.GetSqlBinary(0).Value;

                                                byte[] message = Encoding.ASCII.GetBytes("<data><![CDATA[" + Convert.ToBase64String(buffer) + "]]></data>");
                                                pipeServer.Write(message, 0, message.Length);
                                            }
                                            catch (Exception err)
                                            {
                                                byte[] message = Encoding.ASCII.GetBytes("<error><![CDATA[" + err.Message + "]]></error>");
                                                pipeServer.Write(message, 0, message.Length);
                                            }
                                        }
                                        break;
                                    case "Decrypt":
                                        {
                                            byte[] message = Convert.FromBase64String(rdr.ReadString());
                                            string s = rdr.ReadString();

                                            if (_password == null)
                                            {
                                                byte[] retMessage = Encoding.ASCII.GetBytes("<error>Missing Password</error>");
                                                pipeServer.Write(retMessage, 0, retMessage.Length);
                                            }

                                            try
                                            {
                                                if (conn.State != ConnectionState.Open)
                                                    conn.Open();
                                                SqlCommand cmm = conn.CreateCommand();
                                                cmm.CommandText = "select convert(nvarchar(max), DecryptByCert(Cert_ID('OSMCertificate'), @encryptedtext, @password))";
                                                cmm.Parameters.Add(new SqlParameter("@encryptedtext", message));
                                                cmm.Parameters.Add(new SqlParameter("@password", _password));
                                                SqlDataReader sqlRdr = cmm.ExecuteReader();
                                                sqlRdr.Read();

                                                if (!sqlRdr.IsDBNull(0))
                                                {
                                                    SqlString unEncrypted = sqlRdr.GetString(0);

                                                    byte[] retMessage = Encoding.ASCII.GetBytes("<data><![CDATA[" + unEncrypted.Value + "]]></data>");
                                                    pipeServer.Write(retMessage, 0, retMessage.Length);
                                                }
                                                else
                                                {
                                                    byte[] retMessage = Encoding.ASCII.GetBytes("<error>Decrypt returned NULL</error>");
                                                    pipeServer.Write(retMessage, 0, retMessage.Length);
                                                }
                                            }
                                            catch (Exception err)
                                            {
                                                byte[] retMessage = Encoding.ASCII.GetBytes("<error><![CDATA[" + err.Message + "]]></error>");
                                                pipeServer.Write(retMessage, 0, retMessage.Length);
                                            }
                                        }
                                        break;
                                    case "SetPassword":
                                        _password = rdr.ReadString();
                                        break;
                                }
                            }
                        }
                        pipeServer.Disconnect();
                    }
                }
            }
            catch (Exception err)
            {
                if (!EventLog.SourceExists("OsmEncryptionService"))
                    EventLog.CreateEventSource("OsmEncryptionService", "Application");

                EventLog.WriteEntry("OsmEncryptionService", "Error in MonitorPipe\n" + err.Message, EventLogEntryType.Error);
            }
        }
    }
}
